//核心业务模块

//依赖
const dgram = require('node:dgram')

//导出
module.exports = {
    getTimeFromNTPServer
}

/*
    从伪NTP服务器获取时间戳
    参数options表示请求的选项，结构: 
    {
        address:string, //服务地址
        port: number, //服务端口
        type: string, //服务类型，默认值'udp4'
        timeout: number //请求的超时时间，毫秒
    }
    返回服务器获取的信息，结构：
    {
        version:number, //版本号
        ts:number, //毫秒时间戳
        cost: number, //往返请求的网络耗时
    }
*/
async function getTimeFromNTPServer(options = {}) {
    const client = await createClient(options)
    const reply = await requestServer(client, options.timeout)
    try {
        client.close()
    } catch (error) {

    }
    return reply
}

//创建udp客户端
function createClient({ address = 'localhost', port = 8080, type = "udp4" }) {
    return new Promise((resolve, reject) => {
        const client = dgram.createSocket(type)
        //连接
        client.connect(port, address, (err) => {
            if (err) {
                reject(err)
                client.close()
            } else {
                resolve(client)
            }
        })
    })
}

//请求服务端
function requestServer(client, timeout = 2000) {
    return new Promise((resolve, reject) => {
        //请求的消息
        const requestMsg = Buffer.from('')
        //设置超时时间
        const timeoutID = setTimeout(() => {
            reject(new Error("timeout"))
        }, timeout)
        //监听错误
        client.on('error', (err) => {
            clearTimeout(timeoutID)
            //回调返回出错信息
            reject(err)
            //关闭
            client.close()
        })
        //记录发送请求前的时刻
        const beginAt = Date.now()
        //监听消息
        client.on('message', (msg) => {
            //计算网络耗时
            const cost = Date.now() - beginAt
            try {
                clearTimeout(timeoutID)
                const { version, ts } = parseServerMessage(msg)
                resolve({
                    version,
                    ts,
                    cost,
                })
            } catch (error) {
                reject(error)
            }
        })
        //发送请求
        client.send(requestMsg, (err) => {
            if (err) {
                reject(err)
                client.close()
            }
        })
    })
}

//解析服务返回的二进制数据
function parseServerMessage(msg) {
    //读取版本
    const version = msg.readUInt8()
    //需求没提用大端还是小端，这里按网络常用的大端字节序写入，客户端也需要用相同字节序读取
    const bigint = msg.readBigUInt64BE(1)
    return {
        version,
        ts: Number(bigint),
    }
}